
#import <appkit/appkit.h>
#import <misckit/MiscSortedList.h>
#import <misckit/MiscAppDefaults.h>
#import "../Preferences.subproj/preferences.h"

#import "decode.h"

#import "NiftyMatrix.h"
#import "DecodeCell.h"
#import "NNTP.h"
#import "MainWindowControl.h"
#import "ArticleSetMatrix.h"
#import "ArticleSet.h"
#import "Article.h"
#import "MatrixScroller.h"

#import "DecoderController.h"

#import "descriptors.h"

@implementation DecoderController : Object

- (void) doDecoding:sender
// Sender is the MainWindowControl. This is activated after the user
// chooses Tools->UUdecode from the menu.
{
	int articlesSelected;
	
	// Keep the main window control around cause we'll need it.
	mainWindowControl = sender;
	
	articlesSelected = [[mainWindowControl articleSet] numSelCells];
	// If multiple articles are selected then we have to do the
	// modal panel.
	
	setDecodeDepositPathname([NXApp defaultValue:DEFAULT_SAVE_PATH]);

	if (articlesSelected > 1) {
		[self showDecodePanel:self];
	}
	else	
	// If only a single article is selected then we can grab
	// the text from the text object.
	if (articlesSelected == 1) {
		Text *articleText = [sender articleText];
		NXStream *textStream;
		int status;
		
		textStream = [articleText stream];
		NXSeek(textStream,0,NX_FROMSTART);		

		uud_start();
		status = uudecode(textStream);

		if (status == -1) {
			NXRunAlertPanel("UUdecode Error", 
				"Could not decode the selected article.", "OK", NULL, NULL);
		}
		else 
		if (status == 0 && uudecode_state() == SKIP_LEADING) {
			NXRunAlertPanel("UUdecode Error", 
				"Could not find the end of article.", "OK", NULL, NULL);
		}
		decode_end();
	}
}

- (void) uudecodeMultipleArticles
// Begin the uudecoding.
{
	DecodeCell * selectedArticleCell;
	int selectedArticleCount;
	int i;
	
	[decodeAndAbortButton setEnabled:NO];
	continueDecoding = YES;
	// Start with the first cell that isn't decoded. Usually that's the first
	// one, but if the user clicks the "Abort" button and then decides to 
	// continue...
//	currentDecodeIndex = 0;

	selectedArticleCount = [selectedArticleMatrix cellCount];	
	for (i=0; i<selectedArticleCount; i++) {
		selectedArticleCell = [selectedArticleMatrix cellAt:i :0];
		if ([selectedArticleCell decodeStatus] == NOT_DECODED) {
			break;
		}
	}
	currentDecodeIndex = i;
	
	// This starts the chain of decoding. If there are more articles
	// and the abort button is not clicked then they will get done in turn.
	if (currentDecodeIndex < selectedArticleCount) {
		uud_start();
		[self uudecodeArticle:[[selectedArticleMatrix cellAt:
			currentDecodeIndex :0] articleIndex]];
	}
}

- (void) uudecodeArticle:(int)articleIndex
// This method uudecodes the given article (the articleIndex is it's place in
// the articleMatrix in the main window), then primes (delay performs) the next 
// not yet decoded article if the abort button hasn't been clicked. 
// This is just so you have something like multithreading without actually 
// having to write it.
{
	// Has the abort button been clicked?
	if (continueDecoding) {
		id nntpServer = [mainWindowControl nntpServer];
		Article * article;
		char * body;
		int selectedArticleCount;
		
		// Get and load the article.
		article = [[mainWindowControl articleMatrix] cellAt:articleIndex :0];
		[nntpServer loadArticleBody:article toString:&body];
		
		if (body) {
			// It's probably not the most efficient thing to put the article
			// in a stream, but that's how the uudecode function now expects
			// it.
			NXStream * articleStream = NXOpenMemory(body, strlen(body),
													NX_READONLY);
			DecodeCell * currentCell;
			int currentStatus;
			int px,py;
				
			currentStatus = uudecode(articleStream);
			NXCloseMemory(articleStream, NX_FREEBUFFER);

			// Should it only be setRead if it's successful? For now as you 
			// can see it marks it read regardless.
			//[article setRead];
			
			// Set the image in the cell to show success or failure.
			currentCell = [selectedArticleMatrix cellAt:currentDecodeIndex :0];
			[currentCell setDecodeStatus:(currentStatus == 0) ? 
					DECODE_SUCCESSFUL : DECODE_FAILED];
			[selectedArticleMatrix getRow:&px andCol:&py ofCell:currentCell];
			[selectedArticleMatrix drawCellAt:px :py];
			[selectedArticleMatrix scrollCellToVisible:px upperOffset:1.5 lowerOffset:1.5];
			
			//stop if error
			if(currentStatus){
				decode_end();
				return;
			}
		}			

		// Set the next cell for uudecoding if there are any more.		
		selectedArticleCount = [selectedArticleMatrix cellCount];
		currentDecodeIndex++;
		if (currentDecodeIndex < selectedArticleCount) {
			DecodeCell * nextCell;
			
			nextCell = [selectedArticleMatrix cellAt:currentDecodeIndex :0];
			// This is probably bad (passing an int to the with argument).
			[self perform:_cmd with:(void*)[nextCell articleIndex] 
				afterDelay:0.0 cancelPrevious:NO];
		}
		// We are done decoding so set the button back to say "Decode".
		else {
			//[decodeAndAbortButton setState:0];
			continueDecoding = NO;
			decode_end();
			[panel close];
			[(ArticleSet *)mainWindowControl markRead:self];
			[NXApp abortModal];
		}
	}
}

- (void) showDecodePanel:sender
{
	if (panel == nil) {
		[NXApp loadNibSection:"ModalDecodingPanel.nib" owner:self];
		[self setupDecodePanel];
	}

	[decodeAndAbortButton setState:0];
	[decodeAndAbortButton setEnabled:YES];
	[self loadMatrixWithArticles];
	[panel orderFront:self];
	[NXApp runModalFor:panel];
}

- (void) setupDecodePanel
// This is called only the first time the nib is loaded so we can swap
// the matrix with a nifty matrix.
{
	NiftyMatrix * niftyMatrix;
	ScrollView * scrollView = [[selectedArticleMatrix superview] superview];
	NXRect scrollBoundsRect;
	NXSize interCellSpacing;
	NXSize cellSize;
		
	// Determine the Matrix bounds.
	[scrollView getBounds:&scrollBoundsRect];
	
	// Prepare a NiftyMatrix to go inside the ScrollView.
	niftyMatrix = [[NiftyMatrix alloc] initFrame:&scrollBoundsRect 
		mode:NX_RADIOMODE cellClass:[DecodeCell class]
		numRows:5 numCols:1];
	
	// Eliminate intercell spacing.  
	interCellSpacing.height = 0.0;
	interCellSpacing.width = 0.0;
	[niftyMatrix setIntercell:&interCellSpacing];
			
	// Set the Cell size to the width of the ScrollView's bounds rect. 
	[niftyMatrix getCellSize:&cellSize];
	cellSize.width = NX_WIDTH (&scrollBoundsRect);
	[niftyMatrix setCellSize:&cellSize];
	[niftyMatrix sizeToCells];
	
	// Set NiftyMatrix sizing parameters.
	[niftyMatrix setAutosizeCells:YES];
	[[niftyMatrix superview] setAutoresizeSubviews:YES];
	[niftyMatrix setAutosizing:NX_WIDTHSIZABLE];
	
	// Scroll when the user clicks in the EnhancedNiftyMatrix and then 
	// drags the mouse out of contentView.
	[niftyMatrix setAutoscroll:YES];
	
	// Stick the matrix in the ScrollView.
	[scrollView setDocView:niftyMatrix];
	[niftyMatrix display];
	[selectedArticleMatrix free];
	selectedArticleMatrix = niftyMatrix;
}

- (void) loadMatrixWithArticles
// Loads the nifty matrix with the selected cells from the article matrix.
// We also sort them so that multipart pics will probably be in the correct
// order.
{
	List * articleList = [[mainWindowControl articleMatrix] cellList];
	MiscSortedList * sortedArticleList = [[MiscSortedList alloc] init];
	Article * articleCell;
	DecodeCell * sortedArticleCell;
	int	i;
	int	sortedArticleCount;
	int oldSortType=[Article sortType];
	
	// Get the selected articles and sort them.
	[sortedArticleList setSortEnabled:NO];
	sortedArticleList = [[mainWindowControl articleMatrix] 
		getSelectedCells:sortedArticleList];
	[Article setSortType:SORT_BY_SUBJECT];
	[sortedArticleList sort];
	[Article setSortType:oldSortType];
	
	// Resize our matrix so it will hold the new selected cells.
	sortedArticleCount = [sortedArticleList count];
	[selectedArticleMatrix renewRows:sortedArticleCount cols:1];

	// Fill the matrix.
	for (i=0; i<sortedArticleCount; i++) {
		sortedArticleCell = [selectedArticleMatrix cellAt:i :0];
		articleCell = [sortedArticleList objectAt:i];
		// The article index is the index of the article in the articleMatrix.
		[sortedArticleCell setArticleIndex:[articleList indexOf:articleCell]];
		[sortedArticleCell setStringValue:
			[articleCell header]->fieldBody[SUBJECT]];
		// Since we might be reusing cells we should set this explicitly.
		[sortedArticleCell setDecodeStatus:NOT_DECODED];
	}
	
	// Scroll to the top and redisplay.
//	[[selectedArticleMatrix window] disableFlushWindow];
	[selectedArticleMatrix scrollCellToVisible:0 :0];
	[selectedArticleMatrix sizeToCells];
	[selectedArticleMatrix display];
//	[[selectedArticleMatrix window] reenableFlushWindow];
//	[[selectedArticleMatrix	window] flushWindow];
	
	// Set our counter to display the number of selected articles.
	[numArticlesTF setIntValue:[selectedArticleMatrix cellCount]];
}

- decodeOrAbortButtonClicked:sender
// The sender is the "Decode" or "Abort" button (the same button with
// a title and alt-title).
{
	// The "Decode" button was clicked.
	if ([sender state] == 1) {
		[self uudecodeMultipleArticles];
	}
	// The "Abort" button was clicked.
	else {	
		continueDecoding = NO;
	}
	return self;
}

- cancelModalPanel:sender
// The cancel button was clicked
{
	continueDecoding = NO;
	decode_end();
	[NXApp stopModal];
	[panel close];

	return self;
}

@end

